package org.eoti.elec;

/**
 * The MIT License
 * <p/>
 * Copyright (c) 2009 Malachi de AElfweald (malachid@gmail.com)
 * <p/>
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to the following conditions:
 * <p/>
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
 * Software.
 * <p/>
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

import org.eoti.elec.db.CircuitDB;
import org.eoti.elec.db.CircuitRegistry;
import org.eoti.mimir.MimirException;
import org.eoti.spec.raijin.v1.*;
import org.eoti.util.TimeUtil;
import org.kohsuke.args4j.*;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class CircuitLoader
{
	@Option(name="-db", usage="specify databaseRoot")
	protected File dbRoot = new File("raiju-db");

	@Option(name="-demo", usage="specifies to do some demo usage")
	protected boolean demo = false;

	// receives whatever non-option arguments are passed
	@Argument
	protected List<String> arguments = new ArrayList<String>();

	protected CircuitRegistry registry;

	public static void main(String[] args)
			throws MimirException
	{
		CircuitLoader cl = new CircuitLoader(args);
	}

	public CircuitLoader(String[] args)
			throws MimirException
	{
		CmdLineParser cmdLineParser = new CmdLineParser(this);
		try{
			cmdLineParser.parseArgument(args);
			if(arguments.size() == 0)
			{
				/**
				 * no arguments passed (or just options), do default settings?
				 * or throw new CmdLineException("no arguments given")
				 */
			}
		} catch(CmdLineException e){
			System.err.format("Error: %s\n", e.getMessage());
			System.err.println("USAGE: java org.eoti.elec.CircuitLoader [options...] arguments...");
			cmdLineParser.printUsage(System.err);
			System.err.println();

			System.err.format(
				"EXAMPLE: java org.eoti.elec.CircuitLoader %s\n",
				cmdLineParser.printExample(ExampleMode.ALL)
			);

			return;
		}

		registry = new CircuitRegistry(dbRoot);
		if(arguments.size() > 0)
		{
			System.out.println("Other arguments:");
			for(String arg : arguments)
				System.out.println("\t" + arg);
		}
		if(demo)doDemo();
	}

	protected CircuitDB getDB(){return registry.getCircuitDB();}

	protected void doDemo()
			throws MimirException
	{
		CircuitConfig nandGate = null;
		try{
			System.out.format("Trying to load the 'nand' gate.\n");
			nandGate = getDB().read("nand");
		}catch(Exception e){
			System.out.format("Exception: %s\nTrying to create the 'nand' gate.\n", e.getMessage());
			nandGate = getDB().createNew("nand");

			ComponentConfig andCC = new ComponentConfig();
			andCC.setName("AND1");
			andCC.setType("and");
			nandGate.getComponents().add(andCC);

			ComponentConfig notCC = new ComponentConfig();
			notCC.setName("NOT1");
			notCC.setType("not");
			nandGate.getComponents().add(notCC);

			PinConfig p1 = new PinConfig();
			p1.setName("IN_A");
			p1.setType( PinType.IN);
			p1.setPinNumber(0);
			nandGate.getPins().add(p1);

			PinConfig p2 = new PinConfig();
			p2.setName("IN_B");
			p2.setType(PinType.IN);
			p2.setPinNumber(1);
			nandGate.getPins().add(p2);

			PinConfig p3 = new PinConfig();
			p3.setName("OUT");
			p3.setType(PinType.OUT);
			p3.setPinNumber(2);
			nandGate.getPins().add(p3);

			WireConfig w1 = new WireConfig();
			WireEndpoint w1f = new WireEndpoint();
			w1f.setPin("IN_A");
			WireEndpoint w1t = new WireEndpoint();
			w1t.setComponent("AND1");
			w1t.setPin("IN_A");
			w1.setFrom(w1f);
			w1.getTo().add(w1t);
			nandGate.getWires().add(w1);

			WireConfig w2 = new WireConfig();
			WireEndpoint w2f = new WireEndpoint();
			w2f.setPin("IN_B");
			WireEndpoint w2t = new WireEndpoint();
			w2t.setComponent("AND1");
			w2t.setPin("IN_B");
			w2.setFrom(w2f);
			w2.getTo().add(w2t);
			nandGate.getWires().add(w2);

			WireConfig w3 = new WireConfig();
			WireEndpoint w3f = new WireEndpoint();
			w3f.setComponent("AND1");
			w3f.setPin("OUT");
			WireEndpoint w3t = new WireEndpoint();
			w3t.setComponent("NOT1");
			w3t.setPin("IN");
			w3.setFrom(w3f);
			w3.getTo().add(w3t);
			nandGate.getWires().add(w3);

			WireConfig w4 = new WireConfig();
			WireEndpoint w4f = new WireEndpoint();
			w4f.setComponent("NOT1");
			w4f.setPin("OUT");
			WireEndpoint w4t = new WireEndpoint();
			w4t.setPin("OUT");
			w4.setFrom(w4f);
			w4.getTo().add(w4t);
			nandGate.getWires().add(w4);

			CircuitSpec nandSpec = new CircuitSpec();
			nandSpec.setDescription("NAND: NOT-AND");
			nandSpec.setPropagation(2);
			nandGate.setSpec(nandSpec);

			getDB().write(nandGate);
		}

		System.out.format("Attempting to display the XML for 'nand'\n");
		getDB().display("nand");

		System.out.format("Attempting to display the TruthTable for 'nand'\n");
		TruthTable tt = new TruthTable(new DigitalCircuit(getDB(), nandGate), System.out);
		while(!tt.isDone()){TimeUtil.Nanosecond.sleep(3);}

//		TruthTable tt = new TruthTable(new ANDGate(), System.out);
//		while(!tt.isDone()){TimeUtil.Nanosecond.sleep(3);}
//		tt = new TruthTable(new ORGate(), System.out);
//		while(!tt.isDone()){TimeUtil.Nanosecond.sleep(3);}
//		tt = new TruthTable(new NOTGate(), System.out);
//		while(!tt.isDone()){TimeUtil.Nanosecond.sleep(3);}
		TickGenerator.getInstance().turnOff();

		/**
		 * This needs greatly re-worked... WAY too complicated!
		 */
//		CircuitConfig andGate = getDB().createNew("AND");
//		PinSet pins = new PinSet();
//		PinConfig and1 = new PinConfig();
//		and1.setName("IN_A");
//		and1.setPinNumber(1);
//		and1.setType(PinType.IN);
//		pins.getPins().add(and1);
//		PinConfig and2 = new PinConfig();
//		and2.setName("IN_B");
//		and2.setPinNumber(2);
//		and2.setType(PinType.IN);
//		pins.getPins().add(and2);
//		PinConfig and3 = new PinConfig();
//		and3.setName("OUT");
//		and3.setPinNumber(3);
//		and3.setType(PinType.OUT);
//		pins.getPins().add(and3);
//		andGate.setPins(pins);
//		TruthTable table = new TruthTable();
//		PinTruth pin1truth000 = new PinTruth();
//		pin1truth000.setName("IN_A");
//		pin1truth000.setState(PinState.LOW);
//		PinTruth pin2truth000 = new PinTruth();
//		pin2truth000.setName("IN_B");
//		pin2truth000.setState(PinState.LOW);
//		PinTruth pin3truth000 = new PinTruth();
//		pin3truth000.setName("OUT");
//		pin3truth000.setState(PinState.LOW);
//		Truth truth000 = new Truth();
//		truth000.getValues().add(pin1truth000);
//		truth000.getValues().add(pin2truth000);
//		truth000.getValues().add(pin3truth000);
//		table.getTruths().add(truth000);
//		andGate.setTable(table);
//		getDB().write(andGate);
//		getDB().display("AND");
	}
}
